package logic;

import java.util.logging.*;
import java.util.Date;

/**
 * 
 * @author Dastan Yessekeyev
 * 
 */
public class Parser {
	private Date date;
	private int timePeriodBetweenOccurrences;
	private int numberOfOccurrences;
	private boolean repetitive;
	private boolean acceptableFormat;
	private final static Logger LOGGER = Logger.getLogger(Parser.class.getName());

	public Parser(String st) {
		try {
			st = DeadlineParser.getRidOfRedundantSpacesAndArticles(st);
			parse(st.trim().toLowerCase());
		} catch (Exception e) {
			acceptableFormat = false;
		}
	}

	/**
	 * this function separates <repetitive part> with <first occurrence>
	 * acceptable formats are: 1) every <repetitive part> starting <first
	 * occurrence> or 2) <first occurrence> every <repetitive part> examples:
	 * <every week for 10 days> starting <from tomorrow>, <after next tuesday>
	 * every <day repeat 10 times>
	 * 
	 * @param st
	 * @throws Exception
	 *           pushes up any encountered exception
	 */
	private void parse(String st) throws Exception {
		reset();
		while (st.startsWith("every "))
			st = st.replaceFirst("every ", "");
		while (st.startsWith("starting "))
			st = st.replaceFirst("starting ", "");
		if (st.contains("every "))// <first occurrence> every <repetitive part>
		{
			date = new DeadlineParser().parseString(st.split("every ", 2)[0]);
			processPeriod(st.split("every ", 2)[1]);
		} else if (st.contains("starting "))
		// <repetitive part> starting <first occurrence>
		{
			date = new DeadlineParser().parseString(st.split("starting ", 2)[1]);
			processPeriod(st.split("starting ", 2)[0]);
		} else {
			date = new DeadlineParser().parseString(st);
		}
	}

	private static final int DEFAULT_VALUE_FOR_OCCURRENCES = 10;

	/**
	 * Processes the repetitive part
	 * 
	 * @param arg
	 * @throws Exception
	 *           if it encounters an exception
	 */
	private void processPeriod(String arg) throws Exception {
		arg = arg.trim().toLowerCase().replaceAll("occurrence", "time");
		arg = arg.replace("times", "time");
		arg = arg.replace("time", "");

		repetitive = true;
		String stPeriodBetweenOccurrences = "";

		if (arg.contains("until ")) {
			stPeriodBetweenOccurrences = DeadlineParser
					.remake(arg.split("until", 2)[0]);
			timePeriodBetweenOccurrences = DeadlineParser.getRange(
					stPeriodBetweenOccurrences, 0);

			Date until;
			String repeatUntil = arg.split("until ", 2)[1];
			if (repeatUntil.contains("end of "))
				until = new DeadlineParser().parseString(repeatUntil.replace("end of ",
						"due "));
			else
				until = new DeadlineParser().parseString("before " + repeatUntil);

			long periodInMilliSecs = (until.getTime() - date.getTime());
			long periodInSecs = periodInMilliSecs / 1000;
			if (periodInSecs < 0) {
				acceptableFormat = false;
				throw new Exception("Until what?");
			}
			numberOfOccurrences = (int) (periodInSecs / timePeriodBetweenOccurrences) + 1;
		} else if (arg.contains("for ")) {
			stPeriodBetweenOccurrences = DeadlineParser
					.remake(arg.split("for ", 2)[0]);
			timePeriodBetweenOccurrences = DeadlineParser.getRange(
					stPeriodBetweenOccurrences, 0);

			String repeatFor = arg.split("for ", 2)[1];
			int periodInSecs = DeadlineParser.getRange(repeatFor, 0);

			numberOfOccurrences = periodInSecs / timePeriodBetweenOccurrences;
		} else if (arg.contains("repeat ")) {
			stPeriodBetweenOccurrences = DeadlineParser.remake(arg
					.split("repeat ", 2)[0]);
			timePeriodBetweenOccurrences = DeadlineParser.getRange(
					stPeriodBetweenOccurrences, 0);

			if (arg.trim().endsWith("time"))
				arg = arg.replaceAll("time", "");
			String repeat = arg.split("repeat ", 2)[1];
			numberOfOccurrences = DeadlineParser.getNumericValue(repeat,
					DEFAULT_VALUE_FOR_OCCURRENCES);
		} else {
			timePeriodBetweenOccurrences = DeadlineParser.getRange(arg, 0);

			numberOfOccurrences = DEFAULT_VALUE_FOR_OCCURRENCES;
		}
	}

	private void reset() {
		timePeriodBetweenOccurrences = 0;
		numberOfOccurrences = 1;
		acceptableFormat = true;
		repetitive = false;
		date = new Date();
	}

	public Date getDate() {
		return date;
	}

	public boolean isRepetitive() {
		return repetitive;
	}

	public int getPeriod() {
		return timePeriodBetweenOccurrences;
	}

	public int getNumberOfOccurrences() {
		return numberOfOccurrences;
	}

	public boolean isInAcceptableFormat() {
		return acceptableFormat;
	}

	private static final String[][] test = {
			{ "every month until the end of 2012 starting from 2011",
					"Sat Jan 01 00:00:00 SGT 2011" },
			{
					"every second week and third day until 31st of December 2010 starting from 1st Oct 2010",
					"Fri Oct 01 00:00:00 SGT 2010" },
			{ "november 2012", "Fri Nov 30 23:59:59 SGT 2012" },
			{ "3 weeks after Dec 2011", "Sat Jan 21 23:59:59 SGT 2012" }, };
	private static final int[][] fields = { { 25, 2592000 }, { 6, 1468800 },
			{ 1, 0 }, { 1, 0 } };

	public static String testParser(String arg[]) throws Exception {
		String st = "";
		for (int i = 0; i < test.length; i++) {
			Parser p = new Parser(test[i][0]);
			if (!p.getDate().toString().equalsIgnoreCase(test[i][1]))
				st += "FAILED to parse element " + i + ". @ date field\n";
			if (!(p.getNumberOfOccurrences() == fields[i][0]))
				st += "FAILED to parse element " + i
						+ ". @ number of occurrences field\n";
			if (!(p.getPeriod() == fields[i][1]))
				st += "FAILED to parse element " + i + ". @ period field\n";
		}
		if (st.isEmpty())
			st = "Passed all tests successfully";
		return st;
	}
}
